
#ifndef _VARARRAY_HPP_
#define _VARARRAY_HPP_

#include <array>
#include <cassert>
#include <cstring>
#include <initializer_list>
#include <string>
// #include <type_traits>
// FORTRAN 格式的索引是 列优先 如 a(2,3,4) 为2行3列4页
// 即一列有两个元素，一行有3个元素  页有四个（页）

// c 格式为 2页 3行 四列  注意区分 行优先

////////////////////////////////////////////////////////////////////////////////////构造函数的继承

// namespace VarArray_new
// {

constexpr int FORTRAN_ARRAY{0};
constexpr int C_ARRAY{1};

template <typename T> struct Assign {
  void operator()(T &a, const T &c) { a = c; }
};

template <typename T> struct Add_assign {
  void operator()(T &a, const T &c) { a += c; }
};
template <typename T> struct Mul_assign {
  void operator()(T &a, const T &c) { a *= c; }
};
template <typename T> struct Minus_assign {
  void operator()(T &a, const T &c) { a -= c; }
};
template <typename T> struct Div_assign {
  void operator()(T &a, const T &c) { a /= c; }
};
// 前置声明
template <typename T, int D> class VarArrayBase;

/**
 * @brief 主模板仅仅是提供声明和检查的作用,通过特例化实现
 *
 * 不支持负的索引
 *
 * @tparam T 数据类型
 * @tparam D 维度
 * @tparam option  1 和 C_ARRAY 代表行优先 C 模式
 *                 0 和 FORTRAN_ARRAY 为列优先 fortran模式
 *                 默认列优先 FORTRAN_ARRAY
 */
template <typename T, int D, int option = FORTRAN_ARRAY> class VarArray {
  static_assert(option == FORTRAN_ARRAY || option == C_ARRAY, "option:\n \
        0 or FORTRAN_ARRAY fortran type array \n \
        1 or C_ARRAY for c type array");
};

/**
 * @brief fortran 格式的数组取值
 *
 * @tparam T
 * @tparam D
 */
template <typename T, int D>
class VarArray<T, D, FORTRAN_ARRAY> : public VarArrayBase<T, D> {
public:
  using base = VarArrayBase<T, D>;
  using index_t = typename base::index_t;
  // 继承构造函数
  using VarArrayBase<T, D>::VarArrayBase;

  T &operator()(index_t i, index_t j) noexcept {
    static_assert(D == 2, "two index, two dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1]);
    return base::elem[j * base::dim[0] + i];
  }
  T &operator()(index_t i, index_t j, index_t k) noexcept {
    static_assert(D == 3, "three index, three dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2]);
    return base::elem[(k * base::dim[1] + j) * base::dim[0] + i];
  }

  T &operator()(index_t i, index_t j, index_t k, index_t m) noexcept {
    static_assert(D == 4, "four index, four dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2] && -1 < m && m < base::dim[3]);
    return base::elem[((m * base::dim[2] + k) * base::dim[1] + j) *
                          base::dim[0] +
                      i];
  }

  const T &operator()(index_t i, index_t j) const noexcept {
    static_assert(D == 2, "two index, two dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1]);
    return base::elem[j * base::dim[0] + i];
  }
  const T &operator()(index_t i, index_t j, index_t k) const noexcept {
    static_assert(D == 3, "three index, three dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2]);
    return base::elem[(k * base::dim[1] + j) * base::dim[0] + i];
  }
  const T &operator()(index_t i, index_t j, index_t k, index_t m) const
      noexcept {
    static_assert(D == 4, "four index, four dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2] && -1 < m && m < base::dim[3]);
    return base::elem[((m * base::dim[2] + k) * base::dim[1] + j) *
                          base::dim[0] +
                      i];
  }

  VarArray &AssignValue(const T &val) noexcept {
    base::base_apply(Assign<T>(), val);
    return *this;
  }
  VarArray &operator=(const T &d) noexcept {
    base::base_apply(Assign<T>(), d);
    return *this;
  }
  VarArray &operator*=(const T &fac) noexcept {
    base::base_apply(Mul_assign<T>(), fac);
    return *this;
  }
  VarArray &operator/=(T fac) noexcept {
    base::base_apply(Div_assign<T>(), fac);
    return *this;
  }
  VarArray &operator+=(T fac) noexcept {
    base::base_apply(Add_assign<T>(), fac);
    return *this;
  }
  VarArray &operator-=(T fac) noexcept {
    base::base_apply(Minus_assign<T>(), fac);
    return *this;
  }
};

template <typename T, int D>
class VarArray<T, D, C_ARRAY> : public VarArrayBase<T, D> {
public:
  using base = VarArrayBase<T, D>;
  using VarArrayBase<T, D>::VarArrayBase;
  using index_t = typename base::index_t;

  T &operator()(index_t i, index_t j) noexcept {
    static_assert(D == 2, "two index, two dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1]);
    return base::elem[i * base::dim[1] + j];
  }
  T &operator()(index_t i, index_t j, index_t k) noexcept {
    static_assert(D == 3, "three index, three dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2]);
    return base::elem[(i * base::dim[1] + j) * base::dim[2] + k];
  }

  T &operator()(index_t i, index_t j, index_t k, index_t m) noexcept {
    static_assert(D == 4, "four index, four dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2] && -1 < m && m < base::dim[3]);
    return base::elem[((i * base::dim[1] + j) * base::dim[2] + k) *
                          base::dim[3] +
                      m];
  }

  const T &operator()(index_t i, index_t j) const noexcept {
    static_assert(D == 2, "two index, two dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1]);
    return base::elem[i * base::dim[1] + j];
  }
  const T &operator()(index_t i, index_t j, index_t k) const noexcept {
    static_assert(D == 3, "three index, three dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2]);
    return base::elem[(i * base::dim[1] + j) * base::dim[2] + k];
  }

  const T &operator()(index_t i, index_t j, index_t k, index_t m) const
      noexcept {
    static_assert(D == 4, "four index, four dimension");
    assert(-1 < i && i < base::dim[0] && -1 < j && j < base::dim[1] && -1 < k &&
           k < base::dim[2] && -1 < m && m < base::dim[3]);
    return base::elem[((i * base::dim[1] + j) * base::dim[2] + k) *
                          base::dim[3] +
                      m];
  }

  VarArray &AssignValue(const T &val) noexcept {
    base::base_apply(Assign<T>(), val);
    return *this;
  }
  VarArray &operator=(const T &d) noexcept {
    base::base_apply(Assign<T>(), d);
    return *this;
  }
  VarArray &operator*=(const T &fac) noexcept {
    base::base_apply(Mul_assign<T>(), fac);
    return *this;
  }
  VarArray &operator/=(T fac) noexcept {
    base::base_apply(Div_assign<T>(), fac);
    return *this;
  }
  VarArray &operator+=(T fac) noexcept {
    base::base_apply(Add_assign<T>(), fac);
    return *this;
  }
  VarArray &operator-=(T fac) noexcept {
    base::base_apply(Minus_assign<T>(), fac);
    return *this;
  }
};

template <typename T, int D> class VarArrayBase {
public:
  typedef int index_t;
  typedef T *iter;
  typedef const T *const_iter;
  typedef T value_type;
  typedef T &reference;
  typedef const T &const_reference;

  // 构造函数和拷贝函数
  VarArrayBase() = default;
  VarArrayBase(std::initializer_list<index_t> dimList);
  VarArrayBase(const VarArrayBase &src) { copy_from(src); }
  VarArrayBase(VarArrayBase &&src) { move_from(src); }
  VarArrayBase &operator=(const VarArrayBase &src);
  VarArrayBase &operator=(VarArrayBase &&src);
  // 分配内存了
  template <typename... Dims> VarArrayBase(Dims... dims);
  /**
   * @brief Construct a new Var Array Base object
   * 
   * @tparam Dims 
   * @param p 
   * @param need_own 是否需要拥有资源
   * @param dims 
   */
  template <typename... Dims> VarArrayBase(T *p, bool need_own, Dims... dims);
  ~VarArrayBase() {
    if (owner)
      delete[] elem;
  }

  // 0 1 2 3
  int Dim(int i) const {
    assert(i < D && -1 < i);
    return dim[i];
  }
  T &operator()(index_t i) noexcept {
    static_assert(D == 1, "one index, one dimension");
    assert(-1 < i && i < dim[0]);
    return elem[i];
  }
  inline const T &operator()(index_t i) const noexcept {
    static_assert(D == 1, "one index, one dimension");
    assert(-1 < i && i < dim[0]);
    return elem[i];
  }
  // 重新分配内存和各维度大小
  template <typename... dimensions>
  bool ReConstruct(dimensions... dims) noexcept {
    static_assert(D == sizeof...(dimensions), "not matched\n");
    dim = {dims...};
    sz = CalcSize();
    delete[] elem;
    return elem = new T[sz];
  }
  // 跟复制函数是不同的，复制函数会清空原来的内存 这会保存原有的内存
  inline void CopyValueFrom(const VarArrayBase &src) noexcept;
  inline void MoveValueFrom(VarArrayBase &&src) noexcept;

  iter begin() noexcept { return elem; }
  const_iter begin() const noexcept { return elem; }
  iter end() noexcept { return elem + sz; }
  const_iter end() const noexcept { return elem + sz; }
  index_t size() const noexcept { return sz; }
  iter data() noexcept { return elem; }
  const_iter data() const noexcept { return elem; }

  template <class F> void base_apply(F f, const T &c) {
    for (index_t i = 0; i < sz; ++i)
      f(elem[i], c);
  }
  // private:
protected:
  // 默认从0开始
  std::array<int, D> dim{};
  index_t sz{};
  T *elem{};
  // 添加引用指针试一试
  mutable bool owner{true};

  void copy_from(const VarArrayBase &src);
  void move_from(VarArrayBase &src);

  int CalcSize() noexcept {
    int tmp{1};
    for (int i = 0; i < D; ++i)
      tmp *= dim[i];
    return tmp;
  }
};

template <typename T, int D>
void VarArrayBase<T, D>::copy_from(const VarArrayBase &src) {
  dim = src.dim;
  sz = src.sz;
  elem = new T[sz];
  assert(elem);
  memcpy(elem, src.elem, sizeof(T) * sz);
}
template <typename T, int D>
void VarArrayBase<T, D>::move_from(VarArrayBase &src) {
  dim = src.dim;
  sz = src.sz;
  elem = src.elem;
  src.sz = 0;
  src.elem = nullptr;
  src.dim[0] = 0;
}
/**
 * @brief Construct a new Var Array< T,  D>:: Var Array object
 *      唯一的可以指定维度初始化
 * @tparam T
 * @tparam D
 * @param dimList
 */
template <typename T, int D>
VarArrayBase<T, D>::VarArrayBase(std::initializer_list<index_t> dimList) {
  index_t size = dimList.size();
  assert(D == size);
  const index_t *beg = dimList.begin();
  index_t tmp{1};
  for (index_t i = 0; i < size; ++i) {
    dim[i] = *beg++;
    tmp *= dim[i];
  }
  sz = tmp;
  elem = new T[sz];
  assert(elem);
}

template <typename T, int D>
template <typename... Dims>
inline VarArrayBase<T, D>::VarArrayBase(Dims... dims) : dim{dims...} {
  static_assert(D == sizeof...(Dims), "not matched\n");
  // 这能运行
  // auto tmp{(... * dims)};
  sz = CalcSize();
  elem = new T[sz];
}
template <typename T, int D>
template <typename... Dims>
VarArrayBase<T, D>::VarArrayBase(T *p, bool need_own, Dims... dims) {
  static_assert(D == sizeof...(Dims), "not matched\n");
  delete [] elem;
  elem = p;
  owner = need_own;
  dim = {dims...};
  sz = CalcSize();
}

// 赋值运算符
template <typename T, int D>
VarArrayBase<T, D> &VarArrayBase<T, D>::operator=(const VarArrayBase &src) {
  if (this == &src)
    return *this;
  delete[] elem;
  copy_from(src);
  return *this;
}
// 移动赋值运算符
template <typename T, int D>
VarArrayBase<T, D> &VarArrayBase<T, D>::operator=(VarArrayBase &&src) {
  if (this == &src)
    return *this;
  delete[] elem;
  move_from(src);
}

template <typename T, int D>
void VarArrayBase<T, D>::CopyValueFrom(const VarArrayBase &src) noexcept {
  if (this == &src)
    return;
  assert(dim == src.dim);
  memcpy(elem, src.elem, sizeof(T) * sz);
}
template <typename T, int D>
void VarArrayBase<T, D>::MoveValueFrom(VarArrayBase &&src) noexcept {
  if (this == &src)
    return;
  assert(dim == src.dim);
  delete[] elem;
  elem = src.elem;
  sz = src.dim[0] = 0;
}

// } // namespace VarArray_new
#endif